#include <stdlib.h>
#include "atomic.h"
#include "config.h"

using namespace nbds::arch;

void check(bool test) {
	if (!test)
		exit(1);
}

int main() {
	/* CAS */
#if __SIZEOF_POINTER__ == 8
	unsigned long a = 0xdeadbeefbadc0ffe;
	unsigned long b = cmp_and_swp(&a, 0xdeadbeefbadc0ffe, 0xffffffffeeeeeeee);
	check(a == 0xffffffffeeeeeeee && b == 0xdeadbeefbadc0ffe);

	b = cmp_and_swp(&a, 0xdeadbeefbadc0ffe, 0xeeeeeeeeffffffff);
	check(a == 0xffffffffeeeeeeee && b == 0xffffffffeeeeeeee);
#endif

	unsigned int a2 = 0xbadc0ffe;
	unsigned int b2 = cmp_and_swp(&a2, 0xbadc0ffe, 0xeeeeeeee);
	check(a2 == 0xeeeeeeee && b2 == 0xbadc0ffe);

	b2 = cmp_and_swp(&a2, 0xbadc0ffe, 0xffffffff);
	check(a2 == 0xeeeeeeee && b2 == 0xeeeeeeee);

	unsigned short a3 = 0x0ffe;
	unsigned short b3 = cmp_and_swp(&a3, (unsigned short)0x0ffe, (unsigned short)0xeeee);
	check(a3 == 0xeeee && b3 == 0x0ffe);

	b3 = cmp_and_swp(&a3, (unsigned short)0x0ffe, (unsigned short)0xffff);
	check(a3 == 0xeeee && b3 == 0xeeee);

	unsigned char a4 = 0xfe;
	unsigned char b4 = cmp_and_swp(&a4, (unsigned char)0xfe, (unsigned char)0xee);
	check(a4 == 0xee && b4 == 0xfe);

	b4 = cmp_and_swp(&a4, (unsigned char)0xfe, (unsigned char)0xff);
	check(a4 == 0xee && b4 == 0xee);

	/* FAA */
#if __SIZEOF_POINTER__ == 8
	a = 0xd00dbddfb0dc0ff0;
	b = ftch_and_add(&a, (unsigned long)0x0ea001100a00000e);
	check(a == 0xdeadbeefbadc0ffe && b == 0xd00dbddfb0dc0ff0);
#endif

	a2 = 0xb0dc0ff0;
	b2 = ftch_and_add(&a2, (unsigned int)0x0a00000e);
	check(a2 == 0xbadc0ffe && b2 == 0xb0dc0ff0);

	a3 = 0xfff0;
	b3 = ftch_and_add(&a3, (unsigned short)0x100e);
	check(a3 == 0x0ffe && b3 == 0xfff0);

	a4 = 0xf0;
	b4 = ftch_and_add(&a4, (unsigned char)0x0e);
	check(a4 == 0xfe && b4 == 0xf0);

	/* XCHG */
#if __SIZEOF_POINTER__ == 8
	a = 0xdeadbeefbadc0ffe;
	b = xchg(&a, (unsigned long)0xffffffffeeeeeeee);
	check(a == 0xffffffffeeeeeeee && b == 0xdeadbeefbadc0ffe);
#endif

	a2 = 0xbadc0ffe;
	b2 = xchg(&a2, (unsigned int)0xeeeeeeee);
	check(a2 == 0xeeeeeeee && b2 == 0xbadc0ffe);

	a3 = 0x0ffe;
	b3 = xchg(&a3, (unsigned short)0xeeee);
	check(a3 == 0xeeee && b3 == 0x0ffe);

	a4 = 0xfe;
	b4 = xchg(&a4, (unsigned char)0xee);
	check(a4 == 0xee && b4 == 0xfe);

#ifdef NBDS_CAS2
	/* CAS2 */
#if __SIZEOF_POINTER__ == 8
	struct vals {
		unsigned long a;
		unsigned long b;
	};
	vals dst;
	vals cmp;
	vals src;

	dst.a = 0xdeadbeefbadc0ffe;
	dst.b = 0xabcdef0123456789;

	cmp.a = 0xdeadbeefbadc0ffe;
	cmp.b = 0xabcdef0123456789;

	src.a = 0xffffffffeeeeeeee;
	src.b = 0xddddddddcccccccc;

	unsigned long c_ret[2];
	cmp_and_swp2(&dst.a, &cmp.a, &src.a, c_ret);
	check(dst.a == 0xffffffffeeeeeeee && dst.b == 0xddddddddcccccccc &&
		c_ret[0] == 0xdeadbeefbadc0ffe && c_ret[1] == 0xabcdef0123456789);

	cmp_and_swp2(&dst.a, &cmp.a, &src.a, c_ret);
	check(dst.a == 0xffffffffeeeeeeee && dst.b == 0xddddddddcccccccc &&
		c_ret[0] == 0xffffffffeeeeeeee && c_ret[1] ==  0xddddddddcccccccc);
#endif

	struct vals2 {
		unsigned int a;
		unsigned int b;
	};
	vals2 dst2;
	vals2 cmp2;
	vals2 src2;

	dst2.a = 0xbadc0ffe;
	dst2.b = 0x23456789;

	cmp2.a = 0xbadc0ffe;
	cmp2.b = 0x23456789;

	src2.a = 0xeeeeeeee;
	src2.b = 0xcccccccc;

	unsigned int c_ret2[2];
	cmp_and_swp2(&dst2.a, &cmp2.a, &src2.a, c_ret2);
	check(dst2.a == 0xeeeeeeee && dst2.b == 0xcccccccc &&
		c_ret2[0] == 0xbadc0ffe && c_ret2[1] == 0x23456789);

	cmp_and_swp2(&dst2.a, &cmp2.a, &src2.a, c_ret2);
	check(dst2.a == 0xeeeeeeee && dst2.b == 0xcccccccc &&
		c_ret2[0] == 0xeeeeeeee && c_ret2[1] == 0xcccccccc);

	struct vals3 {
		unsigned short a;
		unsigned short b;
	};
	vals3 dst3;
	vals3 cmp3;
	vals3 src3;

	dst3.a = 0x0ffe;
	dst3.b = 0x6789;

	cmp3.a = 0x0ffe;
	cmp3.b = 0x6789;

	src3.a = 0xeeee;
	src3.b = 0xcccc;

	unsigned short c_ret3[2];
	cmp_and_swp2(&dst3.a, &cmp3.a, &src3.a, c_ret3);
	check(dst3.a == 0xeeee && dst3.b == 0xcccc &&
		c_ret3[0] == 0x0ffe && c_ret3[1] == 0x6789);

	cmp_and_swp2(&dst3.a, &cmp3.a, &src3.a, c_ret3);
	check(dst3.a == 0xeeee && dst3.b == 0xcccc &&
		c_ret3[0] == 0xeeee && c_ret3[1] == 0xcccc);

	struct vals4 {
		unsigned char a;
		unsigned char b;
	};
	vals4 dst4;
	vals4 cmp4;
	vals4 src4;

	dst4.a = 0xfe;
	dst4.b = 0x89;

	cmp4.a = 0xfe;
	cmp4.b = 0x89;

	src4.a = 0xee;
	src4.b = 0xcc;

	unsigned char c_ret4[2];
	cmp_and_swp2(&dst4.a, &cmp4.a, &src4.a, c_ret4);
	check(dst4.a == 0xee && dst4.b == 0xcc &&
		c_ret4[0] == 0xfe && c_ret4[1] == 0x89);

	cmp_and_swp2(&dst4.a, &cmp4.a, &src4.a, c_ret4);
	check(dst4.a == 0xee && dst4.b == 0xcc &&
		c_ret4[0] == 0xee && c_ret4[1] == 0xcc);

#endif
}
